package z1captcha

import (
	"fmt"
	"image/color"
	"math/rand"
	"time"

	"gitee.com/z1gotool/z1crypto"
	"gitee.com/z1gotool/z1err"
	"github.com/mojocn/base64Captcha"
)

func Generate(opt ...CaptchaOpt) (reqId, captcha, b64s string) {
	optTmp := GetCaptchaOpt(opt...)
	reqId, captcha = GenerateText(optTmp)

	if `DrawCaptcha` != `` {
		driver := base64Captcha.NewDriverString(
			optTmp.Height,
			optTmp.Width,
			optTmp.NoiseCount,
			optTmp.ShowLineOptions,
			optTmp.Length,
			`0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`,
			// &color.RGBA{R: 255, G: 255, B: 255, A: 255},
			optTmp.BgColor,
			[]string{},
		)

		item, err := driver.ConvertFonts().DrawCaptcha(captcha)
		z1err.Check(err)
		b64s = item.EncodeB64string()

		// log.Println(`--------b64s-------`, b64s)
	}

	return
}

func Verify(reqId, captcha string, opt ...CaptchaOpt) (ret bool) {

	optTmp := GetCaptchaOpt(opt...)
	{
		if optTmp.Length > 32 {
			optTmp.Length = 32
		}
	}

	timeFlag := time.Now().Unix()
	optTmp.Timeout = optTmp.Timeout / optTmp.TimeoutPeriodSteps
	timeFlag = timeFlag / optTmp.Timeout

	timeoutPeriodSteps := optTmp.TimeoutPeriodSteps + 1
	for i := 0; i < int(timeoutPeriodSteps); i++ {
		timeFlag2 := int(timeFlag) + i
		str := reqId + fmt.Sprintf(`%v`, timeFlag2) + optTmp.Password
		str = z1crypto.Md5(str)
		str = z1crypto.Base64Encode(str)
		captchaTmp := str[0:optTmp.Length]

		if captchaTmp == captcha {
			return true
		}

		// log.Println(`-------Verify------`, i, reqId, timeFlag2, optTmp.Password, optTmp.TimeoutPeriodSteps, captcha, captchaTmp)
	}

	return
}

func GenerateText(opt ...CaptchaOpt) (reqId, captcha string) {
	/*
		验证码生成的简单算法
		str=base64(md5(md5(now_ns))+(now/timeout)+password))
		captcha=str[0:captchaLength]

		md5(now_ns)标识使用位置，生成返回给客户端，验证的时候带过来。
		now/timeout标识使用时间，过期周期数e,生成的时候需要加n，验证的时候，需从e验证到e+n,timeout<有效时间<（n+1）timeout,建议n取4。若n取4，那么当超时时间为5timeout时，timeout<有效时间<5timeout，小的有效时间，就是timeout，为5timeout的百分之二十
		password标识软件系统
		base64把md5的每位16中可能变为64中可能。需要吧base64中的+和/剔除
	*/

	optTmp := GetCaptchaOpt(opt...)
	{
		if optTmp.Length > 32 {
			optTmp.Length = 32
		}
	}

	reqId = fmt.Sprintf(`%v`, time.Now().UnixNano()+rand.Int63n(100))
	reqId = z1crypto.Md5(reqId)

	timeFlag := ``
	{
		tmp := time.Now().Unix()
		timeout := optTmp.Timeout / optTmp.TimeoutPeriodSteps
		tmp = tmp / timeout
		tmp = tmp + optTmp.TimeoutPeriodSteps
		timeFlag = fmt.Sprintf(`%v`, tmp)
	}

	{
		str := reqId + timeFlag + optTmp.Password
		str = z1crypto.Md5(str)
		str = z1crypto.Base64Encode(str)

		captcha = str[0:optTmp.Length]
		// log.Println(`-------GenerateText------`, reqId, timeFlag, optTmp.Password, optTmp.Timeout, optTmp.TimeoutPeriodSteps, captcha)
	}

	return
}

func GetCaptchaOpt(opt ...CaptchaOpt) (optRet CaptchaOpt) {
	optTmp := CaptchaOpt{
		Length:  6,
		Timeout: 60,
		// Source: `1a张三李四王五田七`,
		Password:           `567myzero1@#$`,
		TimeoutPeriodSteps: 4,

		Height:          60,
		Width:           180,
		NoiseCount:      0,
		ShowLineOptions: 14,
		BgColor:         &color.RGBA{R: 255, G: 255, B: 255, A: 255},
	}

	if len(opt) > 0 {
		if opt[0].Length == 0 {
			opt[0].Length = optTmp.Length
			if opt[0].Length > 8 {
				opt[0].Length = 8
			}
		}
		if opt[0].Timeout == 0 {
			opt[0].Timeout = optTmp.Timeout
		}
		if opt[0].TimeoutPeriodSteps == 0 {
			opt[0].TimeoutPeriodSteps = optTmp.TimeoutPeriodSteps
		}
		if opt[0].Password == `` {
			opt[0].Password = optTmp.Password
		}

		if opt[0].Height == 0 {
			opt[0].Height = optTmp.Height
		}
		if opt[0].Width == 0 {
			opt[0].Width = optTmp.Width
		}
		if opt[0].NoiseCount == 0 {
			opt[0].NoiseCount = optTmp.NoiseCount
		}
		if opt[0].ShowLineOptions == 0 {
			opt[0].ShowLineOptions = optTmp.ShowLineOptions
		}
		if opt[0].BgColor == nil {
			opt[0].BgColor = optTmp.BgColor
		}

		optTmp = opt[0]
	}

	optRet = optTmp

	return
}

type CaptchaOpt struct {
	Length             int    // 验证码长度
	Password           string // 验证码密码
	Timeout            int64  // 验证码超时时间
	TimeoutPeriodSteps int64  // 验证码超时时间周期分步

	// Height png height in pixel.
	Height int
	// Width Captcha png width in pixel.
	Width int
	//NoiseCount text noise count.
	NoiseCount int
	//ShowLineOptions := OptionShowHollowLine | OptionShowSlimeLine | OptionShowSineLine .
	ShowLineOptions int
	//BgColor captcha image background color (optional)
	BgColor *color.RGBA
}
